/*
* 手写promise思路
 1. 普通实现 promise ： 两个函数（resolve,reject，用来改变状态的），三个状态（pending,fulfilled,rejected），两个回调(successCallBack,failCallBack，用来todo)。
 2. 实现异步 ： 回调函数定义 与 存储
 3. 实现多次调用then ： 回调函数是个数组，按顺序调用 shift()
 4. 实现then 链式调用 ： 如何实现链式调用（then 方法返回的应该也是个promise 对象，才可以继续使用then 方法）；如何获取前面返回的值做参数（把then中成功回调的返回值，传入到下一个promise对象的resolve方法中）
    如果上一个 then 的返回值是 普通值，以上方法可以执行，如果返回的是个 promise 对象，那要进行处理（处理 promise 函数，因为每种状态都要调用判断，故提出为公共函数 resolvePromise）
 5. 防止自己返回自己的情况，循环调用。在then中用判断 自己返回自己，用reject抛出错误；reason.message可以获取到抛出的错误信息。
 6. 失败抛错，失败状态补充和等待状态补充。try{}catch(){}。等待状态中，要捕捉失败情况，push方法中不直接传入回调函数，而是用传入一个方法，在方法中对回调函数进行处理
 7. 如果then方法没有传入回调函数，则默认回调状态参数，即 then() 相当于 then(value=>value) 或 then(reason=>{ throw reason})
 8. Promise.all （解决异步并发）方法实现：promise.all入参是一个数组，返回的是promise对象。promise.all(['a','b','c'])。all方法的所有入参是成功的，最终状态才是成功的，有一个失败就是失败的。all是一个静态方法。
    如果promise.all参数里面有异步的promise对象，要等待处理完再执行resolve。解决异步并发
 9. Promise.resolve （将给定的值转化成promise对象）。
 10. finally 方法实现，无论成功或者失败都会取回调函数（方法）。返回promise对象，无论是成功还是失败都要返回
 11. catch 方法实现，处理当前promise对象的错误信息。调用then方法，但是只注册失败回调，不注册成功回调。
*/

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected'

class MyPromise {
    constructor(executor) {
        try {
            // 执行器直接执行
            executor()
        } catch (e) {
            // 执行器内报错，状态改成失败
            this.reject(e)
        }
    }
    // 设置promise状态，默认状态为等待
    status = PENDING;
    // then成功回调函数的参数
    value = undefined;
    // then失败回调函数的参数
    reason = undefined;
    // 成功回调函数（异步）多次调用
    succussCallBack = [];
    // 失败回调函数（异步）多次调用
    failCallBack = [];

    resolve = (value) => {
        // 如果状态不是等待，不可执行下一步
        if (this.status !== PENDING) return;
        // 更改状态为成功
        this.status = FULFILLED;
        // 把成功回调函数的参数保存起来
        this.value = value;
        // 判断成功回调函数是否存在，存在就调用（异步）多次调用
        // this.successCallBack && this.successCallBack(this.value)
        while (this.successCallBack.length) {
            this.successCallBack.shift()(this.value)
        }
    }
    reject = (reason) => {
        // 如果状态不是等待，不可执行下一步
        if (this.status !== PENDING) return;
        // 更改状态为失败
        this.status = REJECTED
        // 把失败回调函数的参数保存起来
        this.reason = reason;
        // 判断失败回调函数是否存在，存在就调用（异步）多次调用
        // this.failCallBack && this.failCallBack(this.value)
        while (this.failCallBack.length) {
            this.failCallBack.shift()(this.value)
        }
    }
    then(succussCallBack, failCallBack) { // 是 promise原型上的方法
        // 7. then的参数
        succussCallBack = succussCallBack ? succussCallBack : value => value
        failCallBack = failCallBack ? failCallBack : reason => { throw reason }

        let promise2 = new promise((resolve, reject) => { // 链式调用，then返回promise对象
            if (this.status === FULFILLED) {
                try {
                    setTimeOut(() => { // 改成异步代码,resolvePromise可以获取到promise2
                        // succussCallBack(this.value)
                        // 把then中成功回调的返回值，传入到下一个promise对象的resolve方法中
                        let x = succussCallBack(this.value)
                        // resolve(x)
                        // 解析 promise，同步代码promise2无法获取到，故改成异步代码，用setTimeOut
                        resolvePromise(promise2, x, resolve, reject)
                    }, 0)
                } catch (e) {
                    reject(e)
                }
            } else if (this.status === REJECTED) {
                // failCallBack(this.reason)
                try {
                    setTimeOut(() => { // 改成异步代码,resolvePromise可以获取到promise2
                        // succussCallBack(this.value)
                        // 把then中成功回调的返回值，传入到下一个promise对象的resolve方法中
                        let x = failCallBack(this.reason)
                        // resolve(x)
                        // 解析 promise，同步代码promise2无法获取到，故改成异步代码，用setTimeOut
                        resolvePromise(promise2, x, resolve, reject)
                    }, 0)
                } catch (e) {
                    reject(e)
                }
            } else {
                // 等待
                // 等待的时候要把回调函数存储起来（异步）
                // this.succussCallBack.push(succussCallBack)
                // this.failCallBack.push(failCallBack)
                // 要捕捉失败情况，push方法中不直接传入回调函数，而是用传入一个方法，在方法中对回调函数进行处理
                this.succussCallBack.push(() => {
                    try {
                        setTimeOut(() => { // 改成异步代码,resolvePromise可以获取到promise2
                            // succussCallBack(this.value)
                            // 把then中成功回调的返回值，传入到下一个promise对象的resolve方法中
                            let x = succussCallBack(this.value)
                            // resolve(x)
                            // 解析 promise，同步代码promise2无法获取到，故改成异步代码，用setTimeOut
                            resolvePromise(promise2, x, resolve, reject)
                        }, 0)
                    } catch (e) {
                        reject(e)
                    }
                })
                this.failCallBack.push(() => {
                    try {
                        setTimeOut(() => { // 改成异步代码,resolvePromise可以获取到promise2
                            // succussCallBack(this.value)
                            // 把then中成功回调的返回值，传入到下一个promise对象的resolve方法中
                            let x = failCallBack(this.reason)
                            // resolve(x)
                            // 解析 promise，同步代码promise2无法获取到，故改成异步代码，用setTimeOut
                            resolvePromise(promise2, x, resolve, reject)
                        }, 0)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
            return promise2;
        })
    }
    // 10. finally 方法实现
    finally(callBack) {
        return this.then(value => {
            callBack();
            return value; // 传递参数
        }, reason => {
            callBack();
            throw reason; // 传递参数
        })
    }
    // 11. catch 方法实现：只注冊失敗回調，不注册成功回调
    catch(failCallBack) {
        return this.then(undefined, failCallBack)
    }
    // 8. Promise.all方法
    static all(array) {
        let result = [];
        let index = 0; // 添加index计数器，为了保证array里面所有元素都执行完毕，再调用resolve
        for (var i = 0; i < array.length; i++) {
            function addData(key, value) {
                result[key] = value;
                index++;
                if (index === array.length) {
                    resolve(result)
                }
            }
            let count = array[i]
            if (count instanceof MyPromise) {
                // promise 对象
                count.then(value => addData(i, value), reason => reject(reason))
            } else {
                // 普通值
                addData(i, array[i])
            }
        }
        // resolve(result) // 未考虑到array中有异步promise对象的元素
    }
    // 9. Promise.resolve方法
    static resolve(value) {
        if (value instanceof MyPromise) {
            return value
        }
        return new MyPromise(resolve => {
            resolve(value)
        })
    }
}

function resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) {
        // then函数，成功回调的返回值就是本身，则抛出错误，阻止程序继续向下执行
        // promiseTest.html 测试
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    if (x instanceof MyPromise) {
        // 为true 就是 x 是 MyPromise 原型上的对象
        // x.then((value)=>resolve(value),(reason)=>reject(reason)) 相当于以下
        x.then(resolve, reject)
    } else {
        // 否则就是普通值
        resolve(x)
    }
}
module.exports = MyPromise;